// -------------------------------
// DistRMI
//
package ResImpl;

import ResInterface.*;
import LockManager.*;

import java.util.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.rmi.*;

import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

//public class DistRMI extends java.rmi.server.UnicastRemoteObject
public class DistRMI implements ResourceManager {

	private static String stateFileName = "rmiState.dat";
	// For enabling restores
	protected RMHashtable reservation_cacheHT = new RMHashtable();

	protected RMHashtable m_itemHT = new RMHashtable();

	private static int mw_port = 3501;
	private static int flights_port = 3502;
	private static int cars_port = 3503;
	private static int hotels_port = 3504;
	private static int customers_port = 3505;

	private static int myPort = flights_port;
	private final static int FLIGHTS = 0;
	private final static int CARS = 1;
	private final static int HOTELS = 2;

	private static ResourceManager mResourceMan;
	private static int type = 0; // Flights by default
	private static String registry_name = "";

	private static LockManager mLockMan;
	private static TransactionManager xManager = new TransactionManager();

	private boolean restore = false;

	// For implementing 2PL - connection to the Middleware
	private static Registry mRegistry;


	public static void main(String args[]) {

		// Figure out where server is running
		String server = "";

		String resourceName = "";

		if (args.length == 2) {
			server = args[1] + ":" + args[0];
			// Second argument is the type "flights", "cars", "hotels", "customers"
			if (args[0].equals("flights")) {
				type = FLIGHTS;
				myPort = flights_port;
				resourceName = "FlightsRM";
			} else if(args[0].equals("cars")) {
				type = CARS;
				myPort = cars_port;
				resourceName = "CarsRM";
			} else if(args[0].equals("hotels")) {
				type = HOTELS;
				myPort = hotels_port;
				resourceName = "HotelsRM";
			} else {
				System.out.println("Wrong usage: DistRMI [ResourceType] [IP address]");
				System.exit(1);
			}

		} else {
			System.out.println ("Wrong usage");
			System.out.println("Usage: java ResImpl.DistRMI [ResourceType] [IP address]");
			System.exit(1);
		}

		registry_name = "rebdav" + resourceName;
		System.out.println("Exporting myself");
		try
		{
			// create a new Server object
			DistRMI obj = new DistRMI();
			// dynamically generate the stub (proxy for the Middleware Server)
			ResourceManager rm = (ResourceManager) UnicastRemoteObject.exportObject(obj, 0);

			// Bind the remote object's stub in the registry on it's designated port
			Registry registry = LocateRegistry.getRegistry(myPort);
			registry.rebind(registry_name, rm);

			System.out.println(registry_name + " server ready.");
			obj.cleanup();
		} 
		catch (Exception e) 
		{
			System.out.println(registry_name + " server exception: " + e.toString());
			System.out.println("Usage: java ResImpl.DistRMI [ResourceType] [IP address]");		
			e.printStackTrace();
		}

		

	}

	/**
	 * Before booting up, the RMs must now check if a log file and object state file exist
	 * If so, they must build with the appropriate state
	 * If not, they must create the files and build normally
	 * 
	 * Also, change the statefile name based on the type of RM
	 * 
	 */
	public DistRMI() throws RemoteException {
		stateFileName = "" + type + stateFileName ;
		mLockMan = new LockManager();

	}

	public boolean connect() throws RemoteException {
		// get the registry of the MiddlewareServer for making requests (d3)
		try
		{

			System.out.println("Trying to get connection to Middleware now...");

			mRegistry = LocateRegistry.getRegistry("localhost", mw_port);

			// get the proxy and the remote reference by rmiregistry lookup
			mResourceMan = (ResourceManager) mRegistry.lookup("rebdavMiddleware");

			if(mResourceMan != null)
			{
				System.out.println("Successful");
				System.out.println("Connected to rebdavMiddleware");
				return true;
			}
			else
			{
				System.out.println("Unsuccessful connection to Middleware");
			}
		}
		catch (Exception e)
		{	
			System.err.println("Client exception: " + e.toString());
			e.printStackTrace();

		}
		return false;
	}	

	// Reads a data item
	private RMItem readData( int xid, String key )
	{
		synchronized(m_itemHT){
			
			return (RMItem) m_itemHT.get(key);
		}
	}

	// Writes a data item
	private void writeData( int xid, String key, RMItem value )
	{
		synchronized(m_itemHT){
			
			m_itemHT.put(key, value);
		}
	}
	
	// Remove the item out of storage
	protected RMItem removeData(int xid, String key){
		synchronized(m_itemHT){
			
			return (RMItem)m_itemHT.remove(key);
		}
	}
	
	// Methods calling this are synchronized already for concurrency protection
	// deletes the entire item
	protected boolean deleteItem(int xid, String key)
	{
		Trace.info("RM::deleteItem(" + xid + ", " + key + ") called" );
		ReservableItem curObj = (ReservableItem) readData( xid, key );
		// Check if there is such an item in the storage
		if( curObj == null ) {
			Trace.warn("RM::deleteItem(" + xid + ", " + key + ") failed--item doesn't exist" );
			return false;
		
		} else {
			if(curObj.getReserved()==0){
				removeData(xid, curObj.getKey());
				Trace.info("RM::deleteItem(" + xid + ", " + key + ") item deleted" );
				
				return true;
			}
			else{
				Trace.info("RM::deleteItem(" + xid + ", " + key + ") item can't be deleted because some customers reserved it" );
				return false;
			}
		} // if
	}
	

	// query the number of available seats/rooms/cars
	protected int queryNum(int xid, String key) {
		Trace.info("RM::queryNum(" + xid + ", " + key + ") called" );
		ReservableItem curObj = (ReservableItem) readData( xid, key);
		int value = 0;  
		if( curObj != null ) {
			value = curObj.getCount();
		} // else
		Trace.info("RM::queryNum(" + xid + ", " + key + ") returns count=" + value);
		
		return value;
	}	
	
	// query the price of an item
	protected int queryPrice(int xid, String key){
		Trace.info("RM::queryCarsPrice(" + xid + ", " + key + ") called" );
		ReservableItem curObj = (ReservableItem) readData( xid, key);
		int value = 0; 
		if( curObj != null ) {
			value = curObj.getPrice();
		} // else
		Trace.info("RM::queryCarsPrice(" + xid + ", " + key + ") returns cost=$" + value );
		
		return value;		
	}
	
	// reserve an item
	protected boolean reserveItem(int xid, int customerID, String key, String location){
		Trace.info("RM::reserveItem( " + xid + ", customer=" + customerID + ", " +key+ ", "+location+" ) called" );		
		// Read customer object if it exists (and read lock it)
		Customer cust = (Customer) readData( xid, Customer.getKey(customerID) );		
		if( cust == null ) {
			Trace.warn("RM::reserveCar( " + xid + ", " + customerID + ", " + key + ", "+location+")  failed--customer doesn't exist" );
			return false;
		}
		
		// check if the item is available
		ReservableItem item = (ReservableItem)readData(xid, key);
		if(item==null){
			Trace.warn("RM::reserveItem( " + xid + ", " + customerID + ", " + key+", " +location+") failed--item doesn't exist" );
			return false;
		}else if(item.getCount()==0){
			Trace.warn("RM::reserveItem( " + xid + ", " + customerID + ", " + key+", " + location+") failed--No more items" );
			return false;
		}else{			
			cust.reserve( key, location, item.getPrice());		
			writeData( xid, cust.getKey(), cust );
			
			// decrease the number of available items in the storage
			item.setCount(item.getCount() - 1);
			item.setReserved(item.getReserved()+1);
			
			Trace.info("RM::reserveItem( " + xid + ", " + customerID + ", " + key + ", " +location+") succeeded" );
			
			return true;
		}		
	}
	
	// Create a new flight, or add seats to existing flight
	//  NOTE: if flightPrice <= 0 and the flight already exists, it maintains its current price
	public boolean addFlight(int xid, int flightNum, int flightSeats, int flightPrice)
		throws RemoteException, DeadlockException
	{
		//Since this is an RM, we dont want to start xactions with arbitrary IDs, want to use the ids created by the
		//middleware
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		
		//Log before Image
		Transaction t = xManager.getTransactionById(xid);
		Object[] state = getPreviousFlightState(xid,flightNum);
		t.addWrite(RecordState.TYPE_OPERATION, Flight.getKey(flightNum),state);
		
		// objects equal if have same strData. So strData must be unique.
		// thread could hang here if there is a lock conflict. when released, does stuff in loop.
		if(mLockMan.Lock(xid, "f:" + flightNum, TrxnObj.WRITE)) {
			Trace.info("RM::addFlight(" + xid + ", " + flightNum + ", $" + flightPrice + ", " + flightSeats + ") called" );
			Flight curObj = (Flight) readData( xid, Flight.getKey(flightNum) );
			if( curObj == null ) {
				// doesn't exist...add it
				Flight newObj = new Flight( flightNum, flightSeats, flightPrice );
				writeData( xid, newObj.getKey(), newObj );
				Trace.info("RM::addFlight(" + xid + ") created new flight " + flightNum + ", seats=" +
						flightSeats + ", price=$" + flightPrice );
			} else {
				// add seats to existing flight and update the price...
				curObj.setCount( curObj.getCount() + flightSeats );
				if( flightPrice > 0 ) {
					curObj.setPrice( flightPrice );
				} // if
				writeData( xid, curObj.getKey(), curObj );
				Trace.info("RM::addFlight(" + xid + ") modified existing flight " + flightNum + ", seats=" + curObj.getCount() + ", price=$" + flightPrice );
			} // else	
		} else {
			// invalid lock type!
			writeStateToDisk();
			System.out.println("Invalid lock type!");
			return false;
		}
		
		writeStateToDisk();
		
		return(true);
	}
	
	private Object[] getPreviousFlightState(int xid,int flightNum) {
		Flight f = (Flight)readData(xid,Flight.getKey(flightNum));
		if(f == null){
			return null;
		}
		else{
			int noSeats = queryNum(xid, Flight.getKey(flightNum));
			int price = queryPrice(xid, Flight.getKey(flightNum));
			Object[] state = {new Integer(flightNum),new Integer(noSeats), new Integer(price)};
			return state;
		}
	}
	
	private Object[] getPreviousCarState(int xid,String location) {
		Car c = (Car)readData(xid,Car.getKey(location));
		if(c == null){
			return null;
		}
		else {
			int noCars = queryNum(xid, Car.getKey(location));
			int price = queryPrice(xid, Car.getKey(location));
			Object[] state = {location,new Integer(noCars), new Integer(price)};
			return state;
		}
	}
	
	private Object[] getPreviousRoomState(int xid, String location) {
		Hotel h = (Hotel)readData(xid,Hotel.getKey(location));
		if(h==null){
			return null;
		}
		else{
			int noRooms = queryNum(xid, Hotel.getKey(location));
			int price = queryPrice(xid, Hotel.getKey(location));
			Object[] state = {location,new Integer(noRooms), new Integer(price)};
			return state;
		}
	}
	

	public synchronized boolean deleteFlight(int xid, int flightNum)
		throws RemoteException, DeadlockException
	{
		
		//Since this is an RM, we dont want to start xactions with arbitrary IDs, want to use the ids created by the
		//middleware
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
			
		}

		boolean result;
		
		//Log before Image
		Transaction t = xManager.getTransactionById(xid);
		Object[] state = getPreviousFlightState(xid,flightNum);
		t.addWrite(RecordState.TYPE_OPERATION, Flight.getKey(flightNum),state);

		if(mLockMan.Lock(xid, "f:" + flightNum, TrxnObj.WRITE)) {
			
			result = deleteItem(xid, Flight.getKey(flightNum));
			writeStateToDisk();
			return result;
		} else {
			System.out.println("Invalid lock type!");
			writeStateToDisk();
			return false;
		}
		
	}



	// Create a new room location or add rooms to an existing location
	//  NOTE: if price <= 0 and the room location already exists, it maintains its current price
	public boolean addRooms(int xid, String location, int count, int price)
		throws RemoteException, DeadlockException
	{

		//Since this is an RM, we dont want to start xactions with arbitrary IDs, want to use the ids created by the
		//middleware
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}

		//Log before Image
		Transaction t = xManager.getTransactionById(xid);
		Object[] state = getPreviousRoomState(xid,location);
		t.addWrite(RecordState.TYPE_OPERATION, Hotel.getKey(location),state);
		
		if(mLockMan.Lock(xid, "r:" + location, TrxnObj.WRITE)) {
			Trace.info("RM::addRooms(" + xid + ", " + location + ", " + count + ", $" + price + ") called" );
			Hotel curObj = (Hotel) readData( xid, Hotel.getKey(location) );
			if( curObj == null ) {
				// doesn't exist...add it
				Hotel newObj = new Hotel( location, count, price );
				writeData( xid, newObj.getKey(), newObj );
				Trace.info("RM::addRooms(" + xid + ") created new room location " + location + ", count=" + count + ", price=$" + price );
			} else {
				// add count to existing object and update price...
				curObj.setCount( curObj.getCount() + count );
				if( price > 0 ) {
					curObj.setPrice( price );
				} // if
				writeData( xid, curObj.getKey(), curObj );
				Trace.info("RM::addRooms(" + xid + ") modified existing location " + location + ", count=" + curObj.getCount() + ", price=$" + price );
			} // else
			writeStateToDisk();
			return true;
		} else {
			writeStateToDisk();
			System.out.println("Invalid lock type!");
			return false;
		}

	}

	// Delete rooms from a location
	public synchronized boolean deleteRooms(int xid, String location)
		throws RemoteException, DeadlockException
	{

		//Since this is an RM, we dont want to start xactions with arbitrary IDs, want to use the ids created by the
		//middleware
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		
		boolean result;

		//Log before Image
		Transaction t = xManager.getTransactionById(xid);
		Object[] state = getPreviousRoomState(xid,location);
		t.addWrite(RecordState.TYPE_OPERATION, Hotel.getKey(location),state);
		
		if(mLockMan.Lock(xid, "r:" + location, TrxnObj.WRITE)) {
			result = deleteItem(xid, Hotel.getKey(location));
			writeStateToDisk();
			return result;
		} else {
			System.out.println("Invalid lock type!");
			writeStateToDisk();
			return false;
		}
		 
		
	}

	// Create a new car location or add cars to an existing location
	//  NOTE: if price <= 0 and the location already exists, it maintains its current price
	public boolean addCars(int xid, String location, int count, int price)
		throws RemoteException, DeadlockException
	{

		//Since this is an RM, we dont want to start xactions with arbitrary IDs, want to use the ids created by the
		//middleware
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}

		//Log before Image
		Transaction t = xManager.getTransactionById(xid);
		Object[] state = getPreviousCarState(xid,location);
		t.addWrite(RecordState.TYPE_OPERATION, Car.getKey(location),state);
		
		if(mLockMan.Lock(xid, "c:" + location, TrxnObj.WRITE)) {
			Trace.info("RM::addCars(" + xid + ", " + location + ", " + count + ", $" + price + ") called" );
			Car curObj = (Car) readData( xid, Car.getKey(location) );
			if( curObj == null ) {
				// car location doesn't exist...add it
				Car newObj = new Car( location, count, price );
				writeData( xid, newObj.getKey(), newObj );
				Trace.info("RM::addCars(" + xid + ") created new location " + location + ", count=" + count + ", price=$" + price );
			} else {
				// add count to existing car location and update price...
				curObj.setCount( curObj.getCount() + count );
				if( price > 0 ) {
					curObj.setPrice( price );
				} // if
				writeData( xid, curObj.getKey(), curObj );
				Trace.info("RM::addCars(" + xid + ") modified existing location " + location + ", count=" + curObj.getCount() + ", price=$" + price );
			} // else
			writeStateToDisk();
			return true;
		} else {
			System.out.println("Invalid lock type.");
			writeStateToDisk();
			return false;
		}
	}


	// Delete cars from a location
	public synchronized boolean deleteCars(int xid, String location)
		throws RemoteException, DeadlockException
	{

		//middleware
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}

		boolean result;
		
		//Log before Image
		Transaction t = xManager.getTransactionById(xid);
		Object[] state = getPreviousCarState(xid,location);
		t.addWrite(RecordState.TYPE_OPERATION, Car.getKey(location), state);
		
		if(mLockMan.Lock(xid, "c:" + location, TrxnObj.WRITE)) {
			result =  deleteItem(xid, Car.getKey(location));
			writeStateToDisk();
			return result;
		} else {
			System.out.println("Invalid lock type");
			writeStateToDisk();
			return false;
		}
		
	}



	// Returns the number of empty seats on this flight
	public int queryFlight(int xid, int flightNum)
		throws RemoteException, DeadlockException
	{
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		writeStateToDisk();
		
		if(mLockMan.Lock(xid, "f:" +flightNum, TrxnObj.READ)) {
			return queryNum(xid, Flight.getKey(flightNum));
		} else {
			System.out.println("Invalid lock type");
			return -1;
		}
		
	}

	// Returns the number of reservations for this flight. 
	public int queryFlightReservations(int xid, int flightNum)
		throws RemoteException, DeadlockException
	{
		
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		writeStateToDisk();
		
		if(mLockMan.Lock(xid, "f:" + flightNum, TrxnObj.READ)) {
			Trace.info("RM::queryFlightReservations(" + xid + ", #" + flightNum + ") called" );
			RMInteger numReservations = (RMInteger) readData( xid, Flight.getNumReservationsKey(flightNum) );
			if( numReservations == null ) {
				numReservations = new RMInteger(0);
			} // if
			Trace.info("RM::queryFlightReservations(" + xid + ", #" + flightNum + ") returns " + numReservations );
			return numReservations.getValue();
		} else {
			System.out.println("Invalid lock type.");
			return -1;
		}
		
	}


	// Returns price of this flight
	public int queryFlightPrice(int xid, int flightNum )
		throws RemoteException, DeadlockException
	{
		
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		writeStateToDisk();
		
		if(mLockMan.Lock(xid, "f:" + flightNum, TrxnObj.READ)) {
			return queryPrice(xid, Flight.getKey(flightNum));
		} else {
			System.out.println("Invalid lock type.");
			return -1;
		}
		
	}


	// Returns the number of rooms available at a location
	public int queryRooms(int xid, String location)
		throws RemoteException, DeadlockException
	{
		
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		writeStateToDisk();
		
		if(mLockMan.Lock(xid, "r:" + location, TrxnObj.READ)) {
			return queryNum(xid, Hotel.getKey(location));
		} else {
			System.out.println("Invalid lock type.");
			return -1;
		}
		
	}
	
	
	// Returns room price at this location
	public int queryRoomsPrice(int xid, String location)
		throws RemoteException, DeadlockException
	{
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		writeStateToDisk();
		
		if(mLockMan.Lock(xid, "r:" + location, TrxnObj.READ)) {
			return queryPrice(xid, Hotel.getKey(location));
		} else {
			System.out.println("Invalid lock type.");
			return -1;
		}
		
	}

	// Returns the number of cars available at a location
	public int queryCars(int xid, String location)
		throws RemoteException, DeadlockException
	{
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		writeStateToDisk();
		
		if(mLockMan.Lock(xid, "c:" + location, TrxnObj.READ)) {
			return queryNum(xid, Car.getKey(location));
		} else {
			System.out.println("Invalid lock type.");
			return -1;
		}
	}


	// Returns price of cars at this location
	public int queryCarsPrice(int xid, String location)
		throws RemoteException, DeadlockException
	{
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		writeStateToDisk();
		
		if(mLockMan.Lock(xid, "c:" + location, TrxnObj.READ)) {
			return queryPrice(xid, Car.getKey(location));
		} else {
			System.out.println("Invalid lock type.");
			return -1;
		}
		
	}

	// Returns data structure containing customer reservation info. Returns null if the
	//  customer doesn't exist. Returns empty RMHashtable if customer exists but has no
	//  reservations.
	public RMHashtable getCustomerReservations(int xid, int customerID)
		throws RemoteException, DeadlockException
	{
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		writeStateToDisk();
		
		if(mLockMan.Lock(xid, "t:" + customerID, TrxnObj.READ)) {
			Trace.info("RM::getCustomerReservations(" + xid + ", " + customerID + ") called" );
			Customer cust = (Customer) readData( xid, Customer.getKey(customerID) );
			if( cust == null ) {
				Trace.warn("RM::getCustomerReservations failed(" + xid + ", " + customerID + ") failed--customer doesn't exist" );
				return null;
			} else {
				return cust.getReservations();
			} // if

		} else {
			System.out.println("Invalid lock type.");
			return null;
		}
	}

	public synchronized void restore_reservations() throws RemoteException {
		// enables restore reservations for the next newCustomer call; calls locked in Middleware
		restore = true;
	}

	// return a bill
	public String queryCustomerInfo(int xid, int customerID)
		throws RemoteException, DeadlockException
	{
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}
		writeStateToDisk();
		
		if(mLockMan.Lock(xid, "t:" + customerID, TrxnObj.READ)) {	
			Trace.info("RM::queryCustomerInfo(" + xid + ", " + customerID + ") called" );
			Customer cust = (Customer) readData( xid, Customer.getKey(customerID) );
			if( cust == null ) {
				Trace.warn("RM::queryCustomerInfo(" + xid + ", " + customerID + ") failed--customer doesn't exist" );
				return "";
			} else {
				String s = cust.printBill();
				Trace.info("RM::queryCustomerInfo(" + xid + ", " + customerID + "), bill follows..." );
				System.out.println( s );
				return s;
			} // if
		}
		return null;
	
	}

  // customer functions
  // new customer just returns a unique customer xidentifier
  public int newCustomer(int xid)
		throws RemoteException, DeadlockException
	{
		Trace.info("INFO: RM::Incorrect usage. Middleware should evoke newCustomer(" + xid + ")." );
		return -1;
	}

	// If restore, get all the reservations and reserve them for given new customer
  public boolean newCustomer(int xid, int customerID )
		throws RemoteException, DeadlockException
	{
	  
	  //middleware
	  if(!xManager.isIdValid(xid)){
		  System.out.println("Starting transaction: " + xid);
		  xManager.startNewTransaction(xid);
	  }

	  //Log before Image
	  Transaction t = xManager.getTransactionById(xid);
	  Object[] state = null;
	  t.addWrite(RecordState.TYPE_RESERVATION, Customer.getKey(customerID),state);

		if(mLockMan.Lock(xid, "t:" + customerID, TrxnObj.WRITE)) {
			Trace.info("INFO: RM::newCustomer(" + xid + ", " + customerID + ") called" );
			Customer cust = (Customer) readData( xid, Customer.getKey(customerID) );
			
			if( cust == null ) {
				cust = new Customer(customerID);
				writeData( xid, cust.getKey(), cust );
				Trace.info("INFO: RM::newCustomer(" + xid + ", " + customerID + ") created a new customer" );
				
				if(restore) {
					if(cust.setReservations(reservation_cacheHT)) {
						return true;
					} else {
						Trace.info("INFO: RM::newCustomer created with restore: failed--could not restore reservations.");
					}
					// Done restoring for this customer; reset.
					restore = false;
				}
				writeStateToDisk();
				return true;
			} else {
				Trace.info("INFO: RM::newCustomer(" + xid + ", " + customerID + ") failed--customer already exists");
				writeStateToDisk();
				return false;
			} // else
		} else {
			System.out.println("Invalid lock type");
			writeStateToDisk();
			return false;
		}
	}


	// Deletes customer from the database. 
	public synchronized boolean deleteCustomer(int xid, int customerID)
			throws RemoteException, DeadlockException
	{
		//middleware
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}

		//Log before Image
		Transaction t = xManager.getTransactionById(xid);
		Customer c = (Customer)readData(xid,Customer.getKey(customerID));
		
		System.out.println("Serializing customer: " + c.getID());
		Customer q;
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			oos.writeObject(c);
			oos.flush();
			oos.close();
			bos.close();
			byte [] byteData = bos.toByteArray();
			ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
			q = (Customer) new ObjectInputStream(bais).readObject();
		} catch (IOException e) {
			e.printStackTrace();
			
			throw new RemoteException("Deepcopy error");
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			throw new RemoteException("Deepcopy error");
			
		}
		
		System.out.println("Deserialized customer: " + q.getID());
		
		Object[] state = new Object[] {new Integer(customerID),q};
		t.addWrite(RecordState.TYPE_RESERVATION, Customer.getKey(customerID),state);
		
		if(mLockMan.Lock(xid, "t:" + customerID, TrxnObj.WRITE)) {
			Trace.info("RM::deleteCustomer(" + xid + ", " + customerID + ") called" );
			Customer cust = (Customer) readData( xid, Customer.getKey(customerID) );
			if( cust == null ) {
				Trace.warn("RM::deleteCustomer(" + xid + ", " + customerID + ") failed--customer doesn't exist" );
				writeStateToDisk();
				return false;
			} else {
				// Save for restoring!
				reservation_cacheHT = cust.getReservations();
				// Increase the reserved numbers of all reservable items which the customer reserved. 
				RMHashtable reservationHT = cust.getReservations();
				for(Enumeration e = reservationHT.keys(); e.hasMoreElements();){		
					String reservedkey = (String) (e.nextElement());
					ReservedItem reserveditem = cust.getReservedItem(reservedkey);
					Trace.info("RM::deleteCustomer(" + xid + ", " + customerID + ") has reserved " + reserveditem.getKey() + " " +  reserveditem.getCount() +  " times"  );
					ReservableItem item  = (ReservableItem) readData(xid, reserveditem.getKey());
					Trace.info("RM::deleteCustomer(" + xid + ", " + customerID + ") has reserved " + reserveditem.getKey() + "which is reserved" +  item.getReserved() +  " times and is still available " + item.getCount() + " times"  );
					item.setReserved(item.getReserved()-reserveditem.getCount());
					item.setCount(item.getCount()+reserveditem.getCount());
				}
				
				// remove the customer from the storage
				removeData(xid, cust.getKey());
				
				Trace.info("RM::deleteCustomer(" + xid + ", " + customerID + ") succeeded" );
				writeStateToDisk();
				return true;
			} // if
		} else {
			System.out.println("Invalid lock type.");
			writeStateToDisk();
			return false;
		}
	}




	// Frees flight reservation record. Flight reservation records help us make sure we
	//  don't delete a flight if one or more customers are holding reservations
	public boolean freeFlightReservation(int xid, int flightNum)
		throws RemoteException, DeadlockException
	{
		if(mLockMan.Lock(xid, "f:" + flightNum, TrxnObj.WRITE)) {
			Trace.info("RM::freeFlightReservations(" + xid + ", " + flightNum + ") called" );
			RMInteger numReservations = (RMInteger) readData( xid, Flight.getNumReservationsKey(flightNum) );
			if( numReservations != null ) {
				numReservations = new RMInteger( Math.max( 0, numReservations.getValue()-1) );
			} // if
			writeData(xid, Flight.getNumReservationsKey(flightNum), numReservations );
			Trace.info("RM::freeFlightReservations(" + xid + ", " + flightNum + ") succeeded, this flight now has "
					+ numReservations + " reservations" );
			return true;
		} else {
			System.out.println("Invalid lock type.");
			return false;
		}
	}
	
	// Adds car reservation to this customer. 
	public boolean reserveCar(int xid, int customerID, String location)
		throws RemoteException, DeadlockException
	{

		//middleware
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}

		//Log before Image
		Transaction t = xManager.getTransactionById(xid);
		Customer c = (Customer)readData(xid,Customer.getKey(customerID));
		Customer q = null;
		System.out.println("Serializing customer: " + c.getID());
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			oos.writeObject(c);
			oos.flush();
			oos.close();
			bos.close();
			byte [] byteData = bos.toByteArray();
			ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
			q = (Customer) new ObjectInputStream(bais).readObject();
		} catch (IOException e) {
			e.printStackTrace();
			
			throw new RemoteException("Deepcopy error");
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			throw new RemoteException("Deepcopy error");
			
		}
		
		System.out.println("Deserialized customer: " + q.getID());
		
		Object[] state = new Object[] {new Integer(customerID),q};
		t.addWrite(RecordState.TYPE_RESERVATION, Customer.getKey(customerID),state);
		
		if(mLockMan.Lock(xid, "c:" + location, TrxnObj.WRITE)) {
			boolean result =  reserveItem(xid, customerID, Car.getKey(location), location);
			writeStateToDisk();
			return result;
		} else {
			System.out.println("Invalid lock type.");
			writeStateToDisk();
			return false;
		}
	}


	// Adds room reservation to this customer. 
	public boolean reserveRoom(int xid, int customerID, String location)
		throws RemoteException, DeadlockException
	{
		//middleware
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}

		//Log before Image
		Transaction t = xManager.getTransactionById(xid);
		Customer c = (Customer)readData(xid,Customer.getKey(customerID));
		System.out.println("Serializing customer: " + c.getID());
		Customer q;
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			oos.writeObject(c);
			oos.flush();
			oos.close();
			bos.close();
			byte [] byteData = bos.toByteArray();
			ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
			q = (Customer) new ObjectInputStream(bais).readObject();
		} catch (IOException e) {
			e.printStackTrace();
			
			throw new RemoteException("Deepcopy error");
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			throw new RemoteException("Deepcopy error");
			
		}
		
		System.out.println("Deserialized customer: " + q.getID());
		
		Object[] state = new Object[] {new Integer(customerID),q};
		t.addWrite(RecordState.TYPE_RESERVATION, Customer.getKey(customerID),state);
		
		if(mLockMan.Lock(xid, "r:" + location, TrxnObj.WRITE)) {
			
			boolean result =  reserveItem(xid, customerID, Hotel.getKey(location), location);
			writeStateToDisk();
			return result;
		} else {
			System.out.println("Invalid lock type.");
			writeStateToDisk();
			return false;
		}
	}

	// Adds flight reservation to this customer.  
	public boolean reserveFlight(int xid, int customerID, int flightNum)
		throws RemoteException, DeadlockException
	{
		//middleware
		if(!xManager.isIdValid(xid)){
			System.out.println("Starting transaction: " + xid);
			xManager.startNewTransaction(xid);
		}

		//Log before Image
		Transaction t = xManager.getTransactionById(xid);
		Customer c = (Customer)readData(xid,Customer.getKey(customerID));
		System.out.println("Serializing customer: " + c.getID());
		Customer q;
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			oos.writeObject(c);
			oos.flush();
			oos.close();
			bos.close();
			byte [] byteData = bos.toByteArray();
			ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
			q = (Customer) new ObjectInputStream(bais).readObject();
		} catch (IOException e) {
			e.printStackTrace();
			
			throw new RemoteException("Deepcopy error");
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			throw new RemoteException("Deepcopy error");
			
		}
		
		System.out.println("Deserialized customer: " + q.getID());
		
		Object[] state = new Object[] {new Integer(c.getID()),q};
		t.addWrite(RecordState.TYPE_RESERVATION, Customer.getKey(customerID),state);
		
		if(mLockMan.Lock(xid, "f:" + flightNum, TrxnObj.WRITE)) {
			boolean result = reserveItem(xid, customerID, Flight.getKey(flightNum), String.valueOf(flightNum));
			writeStateToDisk();
			return result;
		} else {
			System.out.println("Invalid lock type.");
			writeStateToDisk();
			return false;
		}
	}
	
	/* reserve an itinerary; not done on the DistRMI level! */
    public boolean itinerary(int xid,int customer,Vector flightNumbers,String location,boolean Car,boolean Room)
	throws RemoteException, DeadlockException {
    	return false;
    }

    /* to release all locks of a transaction, for commit */
    public boolean releaseTransaction(int xid) {
    	return mLockMan.UnlockAll(xid);
    }

    
    /* no need to implement */
	@Override
	public int start() throws RemoteException {
		// TODO Auto-generated method stub
		return 0;
	}

	/**
	 * This method is called by the middleware when a transaction is to be committed
	 * Before returning, the method must write the current state of the hashtables to disk
	 */
	public boolean commit(int transactionId) throws RemoteException,
			TransactionAbortedException, InvalidTransactionException {
		System.out.println("Committing transaction: " + transactionId);
		Transaction t = xManager.getTransactionById(transactionId);
		if(t!=null){
			if(t.isCommited()){
				System.out.println("Ignoring duplicate commit signal for transaction: " + transactionId);
			}
		}
		xManager.commit(transactionId);
		System.out.println("Releasing locks");
		releaseTransaction(transactionId);
		writeStateToDisk();
		return true;
	}

	/**
	 * This method is called by the middleware when a transaction is to be aborted.
	 */
	public void abort(int transactionId) throws RemoteException,
			InvalidTransactionException {
		
		System.out.println("Aborting transaction: " + transactionId);
		
		Transaction t = xManager.getTransactionById(transactionId);
		if(t==null){
			//just in case
			System.out.println("No changes made for transaction ID: " + transactionId);
			System.out.println("Releasing locks");
			releaseTransaction(transactionId);
			writeStateToDisk();
			return;
		}
		else{
			//must check reservations as well
			LinkedList<RecordState> stateList = t.getOperations(transactionId);
			for(int i = 0;i<stateList.size();i++){
				RecordState r = stateList.get(i);
				Object[] args = r.getValues();
				switch(type){
				//for each case, can either have a record operation or a reservation operation.  
				case HOTELS:
					if(r.getRecordType()==RecordState.TYPE_OPERATION){
						if(args==null){
							System.out.println("Removing Room: " + r.getKey());
							deleteItem(transactionId,r.getKey());
						}
						else{
							String hotelLocation = (String) args[0];
							int noRooms = 	((Integer) args[1]).intValue();
							int hotelPrice = ((Integer) args[2]).intValue();
							System.out.println("Restoring room: (" + hotelLocation + "," + noRooms + "," + hotelPrice + ")");
							deleteItem(transactionId, Hotel.getKey(hotelLocation));
							Hotel newHotel = new Hotel( hotelLocation, noRooms, hotelPrice );
							writeData( transactionId, newHotel.getKey(), newHotel );
						}
					}
					else{
						if(args==null){
							System.out.println("Removing customer: " + r.getKey());
							Customer cust = (Customer) readData( transactionId, r.getKey() );
							RMHashtable reservationHT = cust.getReservations();
							for(Enumeration e = reservationHT.keys(); e.hasMoreElements();){		
								String reservedkey = (String) (e.nextElement());
								ReservedItem reserveditem = cust.getReservedItem(reservedkey);
								Trace.info("RM::deleteCustomer(" + transactionId + ", " + r.getKey() + ") has reserved " + reserveditem.getKey() + " " +  reserveditem.getCount() +  " times"  );
								ReservableItem item  = (ReservableItem) readData(transactionId, reserveditem.getKey());
								Trace.info("RM::deleteCustomer(" + transactionId + ", " + r.getKey() + ") has reserved " + reserveditem.getKey() + "which is reserved" +  item.getReserved() +  " times and is still available " + item.getCount() + " times"  );
								item.setReserved(item.getReserved()-reserveditem.getCount());
								item.setCount(item.getCount()+reserveditem.getCount());
							}
							
							// remove the customer from the storage
							removeData(transactionId, cust.getKey());
						}
						else{
							int customerId = ((Integer)args[0]).intValue();
							Customer cust = (Customer)args[1];
							Customer oldCust = (Customer) readData( transactionId, Customer.getKey(customerId));
							System.out.println("Restoring state of customer: " + customerId);
							RMHashtable reservationHT = oldCust.getReservations();
							for(Enumeration e = reservationHT.keys(); e.hasMoreElements();){		
								String reservedkey = (String) (e.nextElement());
								ReservedItem reserveditem = oldCust.getReservedItem(reservedkey);
								ReservableItem item  = (ReservableItem) readData(transactionId, reserveditem.getKey());
								item.setReserved(item.getReserved()-reserveditem.getCount());
								item.setCount(item.getCount()+reserveditem.getCount());
							}
							
							removeData(transactionId, Customer.getKey(customerId));
							writeData(transactionId,cust.getKey(),cust);
						}
					}

					break;
				case CARS:
					if(r.getRecordType()==RecordState.TYPE_OPERATION){
						if(args==null){
							System.out.println("Removing car: " + r.getKey());
							deleteItem(transactionId,r.getKey());
						}
						else{
							String carLocation = (String) args[0];;
							int noCars = ((Integer) args[1]).intValue();
							int carPrice = ((Integer) args[2]).intValue();
							System.out.println("Restoring car: (" + carLocation + "," + noCars + "," + carPrice + ")");
							deleteItem(transactionId, Car.getKey(carLocation));
							Car newCar = new Car( carLocation, noCars, carPrice );
							writeData( transactionId, newCar.getKey(), newCar );
						}
					}
					else{
						if(args==null){
							System.out.println("Removing customer: " + r.getKey());
							Customer cust = (Customer) readData( transactionId, r.getKey() );
							RMHashtable reservationHT = cust.getReservations();
							for(Enumeration e = reservationHT.keys(); e.hasMoreElements();){		
								String reservedkey = (String) (e.nextElement());
								ReservedItem reserveditem = cust.getReservedItem(reservedkey);
								Trace.info("RM::deleteCustomer(" + transactionId + ", " + r.getKey() + ") has reserved " + reserveditem.getKey() + " " +  reserveditem.getCount() +  " times"  );
								ReservableItem item  = (ReservableItem) readData(transactionId, reserveditem.getKey());
								Trace.info("RM::deleteCustomer(" + transactionId + ", " + r.getKey() + ") has reserved " + reserveditem.getKey() + "which is reserved" +  item.getReserved() +  " times and is still available " + item.getCount() + " times"  );
								item.setReserved(item.getReserved()-reserveditem.getCount());
								item.setCount(item.getCount()+reserveditem.getCount());
							}
							
							// remove the customer from the storage
							removeData(transactionId, cust.getKey());
						}
						else{
							int customerId = ((Integer)args[0]).intValue();
							Customer cust = (Customer)args[1];
							Customer oldCust = (Customer) readData( transactionId, Customer.getKey(customerId));
							System.out.println("Restoring state of customer: " + customerId);
							RMHashtable reservationHT = oldCust.getReservations();
							for(Enumeration e = reservationHT.keys(); e.hasMoreElements();){		
								String reservedkey = (String) (e.nextElement());
								ReservedItem reserveditem = oldCust.getReservedItem(reservedkey);
								ReservableItem item  = (ReservableItem) readData(transactionId, reserveditem.getKey());
								item.setReserved(item.getReserved()-reserveditem.getCount());
								item.setCount(item.getCount()+reserveditem.getCount());
							}
							
							removeData(transactionId, Customer.getKey(customerId));
							writeData(transactionId,cust.getKey(),cust);
						}
					}
					break;
				case FLIGHTS:
					if(r.getRecordType()==RecordState.TYPE_OPERATION){
						if(args==null){
							System.out.println("Removing flight: " + r.getKey());
							deleteItem(transactionId, r.getKey());
						}
						else{
							int flightNumber = ((Integer) args[0]).intValue();
							int flightSeats = ((Integer) args[1]).intValue();
							int flightPrice = ((Integer) args[2]).intValue();
							System.out.println("Restoring flight: (" + flightNumber + "," + flightSeats + "," + flightPrice + ")");
							deleteItem(transactionId, Flight.getKey(flightNumber));
							Flight newFlight = new Flight( flightNumber, flightSeats, flightPrice );
							writeData( transactionId, newFlight.getKey(), newFlight );
						}
					}
					else{
						if(args==null){
							System.out.println("Removing customer: " + r.getKey());
							Customer cust = (Customer) readData( transactionId, r.getKey() );
							RMHashtable reservationHT = cust.getReservations();
							for(Enumeration e = reservationHT.keys(); e.hasMoreElements();){		
								String reservedkey = (String) (e.nextElement());
								ReservedItem reserveditem = cust.getReservedItem(reservedkey);
								ReservableItem item  = (ReservableItem) readData(transactionId, reserveditem.getKey());
								item.setReserved(item.getReserved()-reserveditem.getCount());
								item.setCount(item.getCount()+reserveditem.getCount());
							}
							
							// remove the customer from the storage
							removeData(transactionId, cust.getKey());
						}
						else{
							int customerId = ((Integer)args[0]).intValue();
							Customer cust = (Customer)args[1];
							Customer oldCust = (Customer) readData( transactionId, Customer.getKey(customerId));
							System.out.println("Restoring state of customer: " + customerId);
							RMHashtable reservationHT = oldCust.getReservations();
							for(Enumeration e = reservationHT.keys(); e.hasMoreElements();){		
								String reservedkey = (String) (e.nextElement());
								ReservedItem reserveditem = oldCust.getReservedItem(reservedkey);
								ReservableItem item  = (ReservableItem) readData(transactionId, reserveditem.getKey());
								item.setReserved(item.getReserved()-reserveditem.getCount());
								item.setCount(item.getCount()+reserveditem.getCount());
							}
							
							removeData(transactionId, Customer.getKey(customerId));
							writeData(transactionId,cust.getKey(),cust);
						}
					}
					break;
				default:
					throw new RemoteException("Server error occured");
				}
			}
			System.out.println("Releasing locks");
			try{
				releaseTransaction(transactionId);
			} catch(NullPointerException e){
				//pokemon!
			}
			t.abort();
			writeStateToDisk();
		}
	}

	@Override
	public boolean shutdown() throws RemoteException {
		System.out.println("Shutting down...");
		Thread t = new Thread(){
			public void run(){
				try {
					System.out.println(5);
					Thread.sleep(1000);
					System.out.println(4);
					Thread.sleep(1000);
					System.out.println(3);
					Thread.sleep(1000);
					System.out.println(2);
					Thread.sleep(1000);
					System.out.println(1);
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.exit(0);
			}
		};
		t.start();
		return true;
	}
	
	/* Everything below this line is for deliv 3 */
	
	/**
	 * Method called by the middleware server to vote on a particular transaction id.
	 * No need to store result
	 * True = vote commit
	 * False = vote abort
	 */
	public boolean vote(int transactionId){
		System.out.println("Vote request received, about to send reply...");
		countDown(3);
		Transaction t = xManager.getTransactionById(transactionId);
		if(t.isAborted()){
			System.out.println("Voting no");
			t.setVoted();
			writeStateToDisk();
			return false;
		}
		else{
			System.out.println("Voting yes");
			t.setVoted();
			writeStateToDisk();
			return true;
		}
		
	}
	
	/**
	 * Method used internally to write the object state to disk
	 * Must write both hashtables as well as the transaction manager
	 */
	private void writeStateToDisk(){
		
		File state = new File(stateFileName);
		FileOutputStream fileOut;
		try {
			fileOut = new FileOutputStream(state);
			ObjectOutputStream objectWriter = new ObjectOutputStream(fileOut);
			objectWriter.writeObject(xManager);
			objectWriter.writeObject(reservation_cacheHT);
			objectWriter.writeObject(m_itemHT);
			objectWriter.flush();
			objectWriter.close();
			
		} catch (FileNotFoundException e) {
			System.out.println("Error finding file...why?");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("General I/O error");
		}
		
	}
	
	/**
	 * Method used internally to read the object state from disk
	 * Must read both hashtables as well as the transaction manager
	 */
	private void readStateFromDisk(){
		File state = new File(stateFileName);
		FileInputStream fileIn;
		try {
			fileIn = new FileInputStream(state);
			ObjectInputStream objectReader = new ObjectInputStream(fileIn);
			xManager = (TransactionManager) objectReader.readObject();
			reservation_cacheHT = (RMHashtable) objectReader.readObject();
			m_itemHT = (RMHashtable) objectReader.readObject();
			objectReader.close();
			
		} catch (FileNotFoundException e) {
			System.out.println("Error finding file...why?");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("General I/O error");
		} catch (ClassNotFoundException e) {
			System.out.println("Error finding the classes");
		}
	}
	
	/**
	 * This method will query the middleware server for a particular transaction's state.
	 * Used if the RM fails before receiving the middleware's decision after a vote
	 */
	public boolean requestTransactionState(int transactionId){
		return false;
	}
	
	/**
	 * Will be called internally by the constructor to comb the transaction manager for any 
	 * transactions with unresolved states
	 */
	public void cleanup(){
		System.out.println("Begin RM startup sequence...");
		File state = new File(stateFileName);
		if(!state.isFile()){
			System.out.println("No previous records found");
		}
		else{
			try {
				waitForMW();
				System.out.println("Request MW to rebind to me...");
				mResourceMan.rebind(this.type);
			} catch (RemoteException e1) {
				// pokemon.
			}
			
			System.out.println("Previous records found, recovering...");
			readStateFromDisk();
			LinkedList<Transaction> list = xManager.getUnresolved();
			for(int i = 0;i<list.size();i++){
				Transaction t = list.get(i);
				System.out.println("Recovering transaction id: " + t.getId());
				//If no vote, abort
				if(!t.hasVoted()){
					System.out.println("Recovery - Aborting transaction: " + t.getId());
					try {
						abort(t.getId());
					} catch (RemoteException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (InvalidTransactionException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} 
				}
				else{
					//Ugly, but this should take care of the middlware intermittently failing
					//after each transaction is restored
					while(true){
						try{
							System.out.println("Retrieving state of transaction: " + t.getId());
							boolean committed = mResourceMan.requestTransactionState(t.getId());
							if(committed){
								System.out.println("Rcovery - Committing transaction: " + t.getId());
								commit(t.getId());
							}
							else{
								System.out.println("Rcovery - Aborting transaction: " + t.getId());
								abort(t.getId());
							}
							break;
						}catch(Exception e){
							waitForMW();
							continue;
						}
					}
					
				}
			}	
		}
		
		
	
	}
	
	/** Busy loop waiting for middleware to come up **/
	private void waitForMW(){
		while(true){
			try{
				System.out.println("Attempting to connect to middlware server");
				connect();
				System.out.println("Connection achieved");
				break;
			}
			catch(Exception e){
				System.out.println("Failure, retrying...");
				try {
					Thread.sleep(300);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				continue;
			}
		}
	}
	
    public boolean crash(String which) throws RemoteException {
    	return false;
    }

    public boolean prepare(int transactionId) 
    	throws RemoteException, TransactionAbortedException, InvalidTransactionException {
    		return false;
    }
  
    public boolean state() throws RemoteException {
		return true;
	}

	/* only for MW */
	public void rebind(int resourceType) throws RemoteException {
		//pokemon.
	}

	@Override
	public void selfDestruct() throws RemoteException {
		System.out.println("Killing self...");
		System.exit(0);
	}
	
	/**
	 * Counts down, whooo!
	 */
	private void countDown(int amount) {
		while(amount>=0){
			System.out.println(amount + "...");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			amount--;
		}
		
	}
	
}
